package 面试代码;

/**
 * 双重检查
 *
 * 优缺点：
 * Double-Check概念是多线程开发中常使用到的,如代码中所示,我们进行了两次if(instance == null)检查,这样就可以保证线程安全了
 * 这样,实例化代码只用执行一次,后面再次访问时,判断if(instance == null),直接return实例化对象,也避免的反复进行方法同步
 * 这种方法线程安全;延迟加载;效率较高
 * 在实际开发中，推荐使用这种单例设计模式
 */

/**
 * 单例模式的双重检查锁（DCL）可能因指令重排导致空指针异常;volatile关键字：禁止指令重排保障了这个问题不会发生;具体看飞书文档设计模式
 */
public class Single {
    //volatile关键字:
    //1. 保证变量的可见性：当一个被volatile关键字修饰的变量被一个线程修改的时候,其他线程可以立刻得到修改之后的结果。
    //   当一个线程向被volatile关键字修饰的变量写入数据的时候，虚拟机会强制它被值刷新到主内存中。
    //   当一个线程用到被volatile关键字修饰的值的时候，虚拟机会强制要求它从主内存中读取。
    //2. 屏蔽指令重排序：指令重排序是编译器和处理器为了高效对程序进行优化的手段;
    //   非常经典的例子是在单例方法中同时对字段加入voliate,就是为了防止指令重排序。
    //   (不使用volatile,对象实例化是原子操作,但分为几步,每一步又不是原子操作)
    private static volatile Single instance = null;
    //使用双重检查
    public static Single getInstance() {
        if(instance == null){
            //只有instance还没有初始化时,才会走到这个分支
            //这里没有锁保护,所以理论上可以有很多线程同时走到这个分支
            synchronized (Single.class){
                //加锁之后才能执行
                //第一个抢到锁的线程,看到的instance是null
                //其他抢到锁的线程,看到的instance不是null
                //保证了instance只会被实例化一次
                if(instance == null){//instance是共享数据
                    instance = new Single();//只在第一次的时候执行
                }
            }
        }
        return instance;
    }
}


